package com.xp.tinyurl.common.utils;

import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class OkHttp3Utils {
    private static final Logger logger = LoggerFactory.getLogger(OkHttp3Utils.class);

    private static final String QUESTION_MARK = "?";

    /**
     * 最大空闲链接数
     */
    private static final int MAX_IDLE_CONNECTIONS = 100;

    /**
     * 空闲连接数生存时间
     */
    private static final long KEEP_ALIVE_DURATION = 20;

    /**
     * 最大链接数
     */
    private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 300;

    /**
     * 增量
     */
    private static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5;

    /**
     * 连接时间
     */
    private static final long CONNECT_TIMEOUT = 5 * 1000;

    /**
     * 读取时间
     */
    private static final long READ_TIMEOUT = 3 * 1000;

    /**
     * client
     */
    private static OkHttpClient OKHTTPCLIENT;

    static {
        ConnectionPool connectionPool = new ConnectionPool(MAX_IDLE_CONNECTIONS, KEEP_ALIVE_DURATION, TimeUnit.MINUTES);

        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(DEFAULT_MAX_TOTAL_CONNECTIONS);
        dispatcher.setMaxRequestsPerHost(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);

        OKHTTPCLIENT = new OkHttpClient.Builder()
                .connectionPool(connectionPool)
                .connectTimeout(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                .readTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS)
                .writeTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS)
                .retryOnConnectionFailure(true)
                .dispatcher(dispatcher)
                .build();
    }

    /**
     * get请求
     *
     * @param url    请求地址
     * @param params json 参数
     * @return
     */
    public static String get(String url, Map params) throws Exception {
        String resultStr = "";

        if (null != params) {
            if (url.contains(QUESTION_MARK)) {
                url = url.concat(parseUrlData(params));
            } else {
                url = url.concat(QUESTION_MARK + parseUrlData(params));
            }
        }
        Request request = new Request.Builder().url(url).build();
        Call call = OKHTTPCLIENT.newCall(request);
        try {
            Response response = call.execute();
            resultStr = response.body().string();
        } catch (IOException e) {
            close(call);
            throw new IOException(e);
        }
        return resultStr;
    }

    /**
     * post请求
     *
     * @param url   请求地址
     * @param param 参数
     * @return 结果
     */
    public static String post(String url, Object param) {
        String resultStr = "";

        //body
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(mediaType, JSONObject.toJSONString(param));

        //请求
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        Call call = OKHTTPCLIENT.newCall(request);
        try {
            Response response = call.execute();
            resultStr = response.body().string();
        } catch (IOException e) {
            close(call);
            logger.error("okhttp post请求异常.", e);
        }
        return resultStr;
    }

    /**
     * post请求，设置heads
     *
     * @param url      请求地址
     * @param param    参数
     * @param headsMap 设置head参数
     * @return 结果
     */
    public static String post(String url, Object param, Map<String, String> headsMap) {
        String resultStr = "";

        //body
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(mediaType, JSONObject.toJSONString(param));

        Request request = null;
        if (null != headsMap && !headsMap.isEmpty()) {
            //请求
            request = new Request.Builder()
                    .url(url)
                    .post(body)
                    .headers(Headers.of(headsMap))
                    .build();
        } else {

            //请求
            request = new Request.Builder()
                    .url(url)
                    .post(body)
                    .build();
        }


        Call call = OKHTTPCLIENT.newCall(request);
        try {
            Response response = call.execute();
            resultStr = response.body().string();
        } catch (IOException e) {
            close(call);
            logger.error("okhttp post请求异常.", e);
        }
        return resultStr;
    }

    /**
     * get 请求，设置heads
     *
     * @param url      请求地址
     * @param params   参数
     * @param headsMap 设置head参数
     * @return 结果
     */
    public static String get(String url, Map params, Map<String, String> headsMap) {
        String resultStr = "";

        if (null != params) {
            if (url.contains(QUESTION_MARK)) {
                url = url.concat(parseUrlData(params));
            } else {
                url = url.concat(QUESTION_MARK + parseUrlData(params));
            }
        }

        //body
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");

        Request request = null;
        if (null != headsMap && !headsMap.isEmpty()) {
            //请求
            request = new Request.Builder()
                    .url(url)
                    .get()
                    .headers(Headers.of(headsMap))
                    .build();
        } else {

            //请求
            request = new Request.Builder()
                    .url(url)
                    .get()
                    .build();
        }


        Call call = OKHTTPCLIENT.newCall(request);
        try {
            Response response = call.execute();
            resultStr = response.body().string();
        } catch (IOException e) {
            close(call);
            logger.error("okhttp post请求异常.", e);
        }
        return resultStr;
    }

    /**
     * 将参数转换为 &key=value 格式拼接
     *
     * @param params
     * @return
     */
    private static final String parseUrlData(Map<String, Object> params) {

        StringBuilder sb = new StringBuilder();
        for (String key : params.keySet()) {
            sb.append("&").append(key).append("=");
            sb.append(String.valueOf(params.get(key)));
        }
        return sb.substring(1);
    }

    /**
     * 关闭connection
     * @param call
     */
    private final static void close(Call call) {
        try {
            call.cancel();
        } catch (Exception e) {
            logger.error("call.cancel异常.", e);
        }
    }
}
